import java.io.Serializable;
import java.util.ArrayList;
import java.util.Scanner;

/**
 * menu
 *
 * @author Yihan Wang
 * @date 2024/04/21
 */
public class Player implements Serializable {

    private int playerScore = 0;
    private ScoreManager scoreManager = new ScoreManager();
    private String inputWord;
    private String name;


    /**
     * Instantiates a new Player.
     */
    public Player() {
    }

    /**
     * Instantiates a new Player.
     *
     * @param name        the player's name
     * @param playerScore the player's score
     */
    public Player(String name, int playerScore) {
        this.name = name;
        this.playerScore = playerScore;
    }

    /**
     * Gets player score.
     *
     * @return the player score
     */
    public int getPlayerScore() {
        return playerScore;
    }

    /**
     * Sets player score.
     *
     * @param playerScore the player score
     */
    public void setPlayerScore(int playerScore) {
        this.playerScore = playerScore;
    }

    /**
     * Gets score manager.
     *
     * @return the score manager
     */
    public ScoreManager getScoreManager() {
        return scoreManager;
    }

    /**
     * Sets score manager.
     *
     * @param scoreManager the score manager
     */
    public void setScoreManager(ScoreManager scoreManager) {
        this.scoreManager = scoreManager;
    }

    /**
     * Gets input word.
     *
     * @return the input word
     */
    public String getInputWord() {
        return inputWord;
    }

    /**
     * Sets input word.
     *
     * @param inputWord the input word
     */
    public void setInputWord(String inputWord) {
        this.inputWord = inputWord;
    }

    /**
     * Sets name.
     *
     * @param name the name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Gets name.
     *
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * Has won boolean.
     *
     * @return the boolean
     */
    public boolean hasWon() {
        return this.playerScore >= GameBoard.winningScore;
    }

    /**
     * Controls the current player for the entire turn, adding points to the player's score
     * @return Player's current turn score
     */
    public void takeTurn() {
        Scanner sc = new Scanner(System.in);
        System.out.println(name + "'s turn.");
        System.out.println("It's your turn!");
        boolean isTurnEnd = false;
        while (!isTurnEnd) {
            //small menu
            System.out.println("Please select a option");
            System.out.println("Place a word         <--1");
            System.out.println("Skip your turn       <--2");

            //Get Options
            int option;
            try {
                option = sc.nextInt();
            } catch (Exception e) {
                sc.nextLine();     //consume the "enter" behind option number
                System.out.println("Please enter the correct option!!");
                continue;
            }

            //Execution options
            switch (option) {
                case 2:
                    System.out.println(this.name + " skipped the round.");
                    isTurnEnd = true;
                    break;
                case 1:

                    int score = placeWord();
                    this.playerScore += score; // Update player score
                    System.out.println(this.name + " scored " + score + " this turn.");
                    isTurnEnd = true;
                    break;
                default:
                    System.out.println("Please enter the correct option!!");
                    break;
            }
        }
    }

    /**
     * Detects the player's input, detects whether it is legal or not,
     * returns the corresponding score after it is legal.
     */
    private int placeWord() {
        Scanner sc = new Scanner(System.in);
        String word;
        int row;
        int col;
        char orientation;
        this.inputWord = "";
        int turnScore = 0;


        // Ensure that the input is legal
        do {
            System.out.println("Enter a word, its First letter's position (row column), orientation ('h' for horizontal, 'v' for vertical)");
            System.out.println("Example: word 10 5 h");
            System.out.println("Enter 2 to skip");
            String allInput = sc.nextLine();
            if (allInput.equalsIgnoreCase("2")) {
                break;
            }
            String[] input = allInput.split(" ");

            // Ensure that there are no garbled entries
            if (input.length != 4) {
                System.out.println("Invalid input!!. Please enter the word, row, column, and orientation(Remember the space).");
                continue;
            }
            try {
                word = input[0].toUpperCase();
                row = Integer.parseInt(input[1]) - 1;
                col = Integer.parseInt(input[2]) - 1;
                orientation = Character.toUpperCase(input[3].charAt(0));
            } catch (NumberFormatException e) {
                System.out.println("Row and column values must be numbers. Please try again!!");
                continue;
            }
            // Make sure the word exists
            if (!WordCheck.isWordExists(word)) {
                System.out.println(word + " word does not exist");
                continue;
            }
            // Ensure that the orientation is entered correctly
            if (orientation != 'H' && orientation != 'V') {
                System.out.println("The orientation must be h / v");
                continue;
            }
            // Make sure it's not out of bounds
            if (row > GameBoard.BOARD_SIZE-1 || row < 0 || col > GameBoard.BOARD_SIZE-1 || col < 0) {
                System.out.println("Please enter the correct coordinates.");
                continue;
            }

            if (orientation == 'H') {
                if (col + word.length() > GameBoard.BOARD_SIZE || row > GameBoard.BOARD_SIZE-1) {
                    System.out.println("The length of the word exceeds the chessboard!!");
                    continue;
                }
            } else if (orientation == 'V') {
                if (row + word.length() > GameBoard.BOARD_SIZE || col > GameBoard.BOARD_SIZE-1) {
                    System.out.println("The length of the word exceeds the chessboard!!");
                    continue;
                }
            } else {
                System.out.println("Please enter the correct letter representing the orientation!!");
                continue;
            }

            //Testing whether the first input overrides the centre
            if (GameBoard.firstWord) {
                if (orientation == 'H') {
                    if (row != GameBoard.BOARD_CENTER || col > GameBoard.BOARD_CENTER || (col + word.length() - 1) < GameBoard.BOARD_CENTER) {
                        System.out.println("First word must cover the center!!");
                        continue;
                    }
                } else if (orientation == 'V') {
                    if (col != GameBoard.BOARD_CENTER || row > GameBoard.BOARD_CENTER || (row + word.length() - 1) < GameBoard.BOARD_CENTER) {
                        System.out.println("First word must cover the center!!");
                        continue;
                    }
                }else {
                    continue;
                }
            }
            // Determine if any word is reused
            int coveredLetterCount = 0;
            if (!GameBoard.firstWord) {
                boolean isCoverdLetter = false;
                if (orientation == 'H') {
                    for (int i = 0; i < word.length(); i++) {
                        if (!GameBoard.isEnterdBoard[row][col + i]) {
                            isCoverdLetter = true;
                            coveredLetterCount++;
                        }
                    }
                } else if (orientation == 'V') {
                    for (int i = 0; i < word.length(); i++) {
                        if (!GameBoard.isEnterdBoard[row + i][col]) {
                            isCoverdLetter = true;
                            coveredLetterCount++;
                        }
                    }
                }
                if (!isCoverdLetter) {
                    System.out.println("words need to build upon the previous letters.");
                    continue;
                }
            }
            if (coveredLetterCount == word.length()) {
                System.out.println("You need to create a new letter on the board instead of using the previous letter completely");
                continue;
            }


            // Calculate if every letter can be legally typed.
            int count = 0;
            if (orientation == 'H') {
                for (int i = 0; i < word.length(); i++) {
                    if (letterCanEntered(word.charAt(i), row, (col + i))) {
                        count++;
                    }
                }
            } else if (orientation == 'V') {
                for (int i = 0; i < word.length(); i++) {
                    if (letterCanEntered(word.charAt(i), (row + i), col)) {
                        count++;
                    }
                }
            }
            if (count != word.length()) {
                System.out.println("The entered word does not match the letters already on the board!!");
                continue;
            }

            // Detecting the legality of newly generated words
            //Copy the word form again
            boolean isNewWordExist = true;
            for (int i = 0; i < GameBoard.BOARD_SIZE; i++) {
                for (int j = 0; j < GameBoard.BOARD_SIZE; j++) {
                    if (GameBoard.board[i][j] != '.' && GameBoard.board[i][j] != '*' &&
                            GameBoard.board[i][j] != '#' && GameBoard.board[i][j] != '+' && GameBoard.board[i][j] != '-') {
                        GameBoard.enteredLetterBoard[i][j] = GameBoard.board[i][j];
                    }else{
                        GameBoard.enteredLetterBoard[i][j] = ' ';
                    }
                }
            }
            // Fill it in first and see.
            if (orientation == 'H') {
                for (int i = 0; i < word.length(); i++) {
                    GameBoard.enteredLetterBoard[row][col + i] = word.charAt(i);
                }
            } else if (orientation == 'V') {
                for (int i = 0; i < word.length(); i++) {
                    GameBoard.enteredLetterBoard[row + i][col] = word.charAt(i);
                }
            }
            turnScore = 0;
            ArrayList<String> wordInfoList = scanNewWords(GameBoard.enteredLetterBoard, GameBoard.isEnterdBoard);
            //Take each scanned word and test it for existence.
            for (int i = 0; i < wordInfoList.size(); i++) {
                String wordInfoString = wordInfoList.get(i);
                System.out.println("You generate newWord " + wordInfoString);
                String[] wordInfo = wordInfoString.split(" ");
                String tempWord = wordInfo[0].toUpperCase();
                int tempRow = Integer.parseInt(wordInfo[1]);
                int tempCol = Integer.parseInt(wordInfo[2]);
                char tempOrientation = Character.toUpperCase(wordInfo[3].charAt(0));
                if (!WordCheck.isWordExists(tempWord)) {
                    System.out.println(tempWord + " word does not exist!!");
                    isNewWordExist = false;
                    break;
                }
                ScoreManager m1 = new ScoreManager();
                turnScore += m1.calculateWordScore(tempWord,tempRow,tempCol,tempOrientation);
            }
            if (!isNewWordExist){
                System.out.println("Please make sure the input words result in every newly generated word existing.");
                continue;
            }


            //If all the words are found to be legal, then print the word.
            this.inputWord = word;
            if (orientation == 'H') {
                for (int i = 0; i < word.length(); i++) {
                    GameBoard.board[row][col + i] = word.charAt(i);
                    GameBoard.enteredLetterBoard[row][col + i] = word.charAt(i);
                    GameBoard.isEnterdBoard[row][col + i] = false;
                }
            } else if (orientation == 'V') {
                for (int i = 0; i < word.length(); i++) {
                    GameBoard.board[row + i][col] = word.charAt(i);
                    GameBoard.enteredLetterBoard[row + i][col] = word.charAt(i);
                    GameBoard.isEnterdBoard[row + i][col] = false;
                }
            }
            GameBoard.firstWord = false;
            break;
        } while (true);

        return turnScore;
    }

    /**
     * Scan new words array list.
     *
     * @param letter    Detected words
     * @param row       Row of the detected word
     * @param col       Columns of detected words
     * @return is the word can be legally entered
     */
    private static boolean letterCanEntered(char letter, int row, int col) {
        if (GameBoard.isEnterdBoard[row][col]) {
            return true;
        } else {
            if (GameBoard.board[row][col] == letter) {
                return true;
            }
        }
        return false;
    }


    /**
     * Scan all new words and put them information in an arraylist
     *
     * @param board         Boards with new words already entered
     * @param isEnterdBoard board with is there old letters
     * @return the word information array list
     */
    public static ArrayList<String> scanNewWords(char[][] board, boolean[][] isEnterdBoard) {

        ArrayList<String> wordInfoList = new ArrayList<>();
        // Horizontal scanning
        for (int i = 0; i < GameBoard.BOARD_SIZE; i++) {
            StringBuilder newWord = new StringBuilder();
            boolean isNewWord = false;
            boolean isFirstLetter = true;
            int row = 0;
            int col = 0;
            char orientation = 'X';
            for (int j = 0; j < GameBoard.BOARD_SIZE; j++) {
                if (board[i][j] != ' ') {
                    if (isEnterdBoard[i][j]) {
                        isNewWord = true;
                    }
                    if (isFirstLetter) {
                        row = i;
                        col = j;
                        orientation = 'H';
                        isFirstLetter = false;
                    }
                    newWord.append(board[i][j]);
                } else if (newWord.length() > 1) {
                    if (isNewWord) {
                        String wordInfo = newWord + " " + row + " " + col + " " + orientation;
                        wordInfoList.add(wordInfo);
                        isNewWord = false;
                    }
                    newWord = new StringBuilder();
                    isFirstLetter = true;
                    row = 0;
                    col = 0;
                    orientation = 'X';
                } else {
                    newWord = new StringBuilder();
                    isFirstLetter = true;
                    row = 0;
                    col = 0;
                    orientation = 'X';
                }
                //If the scan reaches the last line, determine whether to enter the current word or not
                if(j == GameBoard.BOARD_SIZE-1){
                    if (newWord.length() > 1) {
                        if (isNewWord) {
                            String wordInfo = newWord + " " + row + " " + col + " " + orientation;
                            wordInfoList.add(wordInfo);
                            isNewWord = false;
                        }
                        newWord = new StringBuilder();
                        isFirstLetter = true;
                        row = 0;
                        col = 0;
                        orientation = 'X';
                    }
                }
            }
        }

        // Vertical scanning
        for (int j = 0; j < GameBoard.BOARD_SIZE; j++) {
            StringBuilder newWord = new StringBuilder();
            boolean isNewWord = false;
            boolean isFirstLetter = true;
            int row = 0;
            int col = 0;
            char orientation = 'X';
            for (int i = 0; i < GameBoard.BOARD_SIZE; i++) {
                if (board[i][j] != ' ') {
                    if (isEnterdBoard[i][j]) {
                        isNewWord = true;
                    }
                    if (isFirstLetter) {
                        row = i;
                        col = j;
                        orientation = 'V';
                        isFirstLetter = false;
                    }
                    newWord.append(board[i][j]);
                } else if (newWord.length() > 1) {
                    if (isNewWord) {
                        String info = newWord + " " + row + " " + col + " " + orientation;
                        wordInfoList.add(info);
                        isNewWord = false;
                    }
                    newWord = new StringBuilder();
                    isFirstLetter = true;
                    row = 0;
                    col = 0;
                    orientation = 'X';
                } else {
                    newWord = new StringBuilder();
                    isFirstLetter = true;
                    row = 0;
                    col = 0;
                    orientation = 'X';
                }
                //If the last column is scanned, determine whether to enter the current word or not
                if(i == GameBoard.BOARD_SIZE-1){
                    if (newWord.length() > 1) {
                        if (isNewWord) {
                            String wordInfo = newWord + " " + row + " " + col + " " + orientation;
                            wordInfoList.add(wordInfo);
                            isNewWord = false;
                        }
                        newWord = new StringBuilder();
                        isFirstLetter = true;
                        row = 0;
                        col = 0;
                        orientation = 'X';
                    }
                }
            }
        }

        return wordInfoList;
    }


}